Instituto Federal de São Paulo (IFSP) Campus Bragança Paulista/SP Análise e Desenvolvimento de Sistemas (ADS) 5o. módulo Profa. Ana Paula Müller Giancoli paulagiancoli@ifsp.edu.br |
Segue uma lista de links para consulta, leitura, treinamento, aprofundamento e aperfeiçoamento.
Git: Instalando o Git.
As instalações serão um pouquinho demoradas.
Instrução de criação do app ReactJS
npx create-react-app amplifyapp
Acessando a pasta
cd amplifyapp
Iniciando a aplicação
npm start
Interrompa a execução do servidor com a aplicação, pressionando ctrl+c, no terminal do VSCode.
Em seguida, execute as instruções:
1. Inicializando o git
git init
2. Definindo o repositório de origem a ser utilizado
git remote add origin git@github.com:username/reponame.git
3. Marcando todos os arquivos e subpastas da pasta amplifyapp
git add .
4. Criando o pacote com as alterações a serem feitas no repositório do Github
git commit -m “initial commit”
5. Efetuando o envio dos arquivos para o repositório no Github
git push origin master
De acordo com AWS, "a Interface de Linhas de Comando (CLI) do Amplify é um conjunto de ferramentas unificado para criar Serviços de Nuvem AWS para seu aplicativo seguindo um fluxo de trabalho simples e guiado".
Observação: este comando pode ser executado em qualquer diretório do Prompt de Comando/Terminal, pois o “-g” indica que o binário será instalado globalmente no sistema.
npm install -g @aws-amplify/cli
amplify configure
Efetue o download do arquivo .csv, pois nele contém os ID da chave de acesso e a Chave de acesso secreta que serão utilizadas para configurar nossa aplicação.
Após a confirmação de criação, retorne para o terminal do VSCode e pressione Enter.
Copie e cole ID da chave de acesso e a Chave de acesso secreta quando forem solicitados.
Selecione Edit backend.
O parâmetro appId é opcional e é usado em dois casos de uso:
amplify init
? Enter a name for the project: amplifyapp
? Enter a name for the environment: dev
? Choose your default editor: Visual Studio Code
? Choose the type of app that youre building: javascript
? What javascript framework are you using: react
? Source Directory Path: src
? Distribution Directory Path: build
? Build Command: npm run-script build
? Start Command: npm run-script start
? Initialize the project with the above configuration? (Y/n) Y
? Do you want to use an AWS profile? Y
? Select the authentication method you want to use: AWS profile
? Please choose the profile you want to use: amplifyprofile
amplify console
Ao concluir com sucesso, será exibido um link com o local onde está sua aplicação disponível para uso. Teste-a.
Algo do tipo https://master.fsdfse.amplifyapp.com/, onde fsdfse cada aplicação terá a sua identificação.
Vamos efetuar uma alteração pequena no nosso App.js para testarmos o funcionamento da aplicaçao no Amplify.
Altere o arquivo src/App.js com este código:
************************
/* Arquivo src/App.js */
************************
import React from 'react';
import logo from './logo.svg';
import './App.css';
function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1>Seja bem vinda Ana Giancoli</h1>
</header>
</div>
);
}
export default App;
npm install aws-amplify @aws-amplify/ui-react
amplify add auth
? Do you want to use the default authentication and security configuration? Default configuration
? How do you want users to be able to sign in? Username
? Do you want to configure advanced settings? No, I am done.
O argumento --yes, ou seu alias -y, suprime os prompts da linha de comando se os padrões estiverem disponíveis e usa os padrões na execução do comando.
Para isso, execute a instrução no terminal ou prompt:
amplify push --y
**************************
/* Arquivo src/index.js */
**************************
import Amplify from 'aws-amplify';
import config from './aws-exports';
Amplify.configure(config);
E o componente AmplifySignOut é reposnsável por renderizar o botão Sign Out (Sair).
Altere o arquivo src/App.js para o código:
************************
/* Arquivo src/App.js */
************************
import React from 'react';
import logo from './logo.svg';
import './App.css';
import { withAuthenticator, AmplifySignOut } from '@aws-amplify/ui-react'
function App() {
return (
<div className="App">
<header>
<img src={logo} className="App-logo" alt="logo" />
<h1>We now have Auth!</h1>
</header>
<AmplifySignOut />
</div>
);
}
export default withAuthenticator(App);
npm install amplify-i18n --save
**************************
/* Arquivo src/index.js */
**************************
import { I18n } from 'aws-amplify';
import AmplifyI18n from 'amplify-i18n';
**************************
/* Arquivo src/index.js */
**************************
const locales = ["en", "fr", "pt-BR"]
AmplifyI18n.configure(locales)
I18n.setLanguage("pt-BR")
npm start
Importante:
Importante:
amplify push --y
De acordo com AWS, "A API que você criará é uma GraphQL que usa o AWS AppSync (um serviço GraphQL gerenciado), que tem o suporte do Amazon DynamoDB (um banco de dados NoSQL).
Para adicionar uma nova api, execute a instrução no prompt ou terminal:
amplify add api
? Please select from one of the below mentioned services: GraphQL
? Provide API name: notesapp
? Choose the default authorization type for the API: API Key
? Enter a description for the API key: listagem de notas
? After how many days from now the API key should expire: 7 (or your preferred expiration)
? Do you want to configure advanced settings for the GraphQL API: No, I am done.
? Do you have an annotated GraphQL schema? No
? Do you want a guided schema creation? Yes
? What best describes your project: Single object with fields
? Do you want to edit the schema now? Yes
? Choose your default editor: Visual Studio Code
type Note @model {
id: ID!
name: String!
description: String
}
amplify push --y
amplify console api
amplify console
************************
/* Arquivo src/App.js */
************************
import React, { useState, useEffect } from 'react';
import './App.css';
import { API } from 'aws-amplify';
import { withAuthenticator, AmplifySignOut } from '@aws-amplify/ui-react';
import { listNotes } from './graphql/queries';
import { createNote as createNoteMutation, deleteNote as deleteNoteMutation } from './graphql/mutations';
const initialFormState = { name: '', description: '' }
function App() {
const [notes, setNotes] = useState([]);
const [formData, setFormData] = useState(initialFormState);
useEffect(() => {
fetchNotes();
}, []);
async function fetchNotes() {
const apiData = await API.graphql({ query: listNotes });
setNotes(apiData.data.listNotes.items);
}
async function createNote() {
if (!formData.name || !formData.description) return;
await API.graphql({ query: createNoteMutation, variables: { input: formData } });
setNotes([ ...notes, formData ]);
setFormData(initialFormState);
}
async function deleteNote({ id }) {
const newNotesArray = notes.filter(note => note.id !== id);
setNotes(newNotesArray);
await API.graphql({ query: deleteNoteMutation, variables: { input: { id } }});
}
return (
<div className="App">
<h1>My Notes App</h1>
<input
onChange={e => setFormData({ ...formData, 'name': e.target.value})}
placeholder="Note name"
value={formData.name}
/>
<input
onChange={e => setFormData({ ...formData, 'description': e.target.value})}
placeholder="Note description"
value={formData.description}
/>
<button onClick={createNote}>Create Note</button>
<div style={{marginBottom: 30}}>
{
notes.map(note => (
<div key={note.id || note.name}>
<h2>{note.name}</h2>
<p>{note.description}</p>
<button onClick={() => deleteNote(note)}>Delete note</button>
</div>
))
}
</div>
<AmplifySignOut />
</div>
);
}
export default withAuthenticator(App);
npm start
De acordo com AWS, :
Adequando nossa aplicação para adicionar a capacidade de associar uma imagem à cada nota.
amplify add storage
? Please select from one of the below mentioned services: Content
? Please provide a friendly name for your resource that will be used to label this category in the project: imagestorage
? Please provide bucket name: <your-unique-bucket-name>
? Who should have access: Auth users only
? What kind of access do you want for Authenticated users? create, read, update, delete
? Do you want to add a Lambda Trigger for your S3 Bucket? N
*********************************************************
/* Arquivo amplify/backend/api/notesapp/schema.graphql */
*********************************************************
type Note @model {
id: ID!
name: String!
description: String
image: String
}
amplify push --y
Uma vez que o backend foi atualizado, é preciso adequar o app React para permitir fazer o upload de imagens e visualizá-las nas notas.
Modificando o arquivo src/App.js:
************************
/* Arquivo src/App.js */
************************
import { API, Storage } from 'aws-amplify';
************************
/* Arquivo src/App.js */
************************
async function onChange(e) {
if (!e.target.files[0]) return
const file = e.target.files[0];
setFormData({ ...formData, image: file.name });
await Storage.put(file.name, file);
fetchNotes();
}
************************
/* Arquivo src/App.js */
************************
async function fetchNotes() {
const apiData = await API.graphql({ query: listNotes });
const notesFromAPI = apiData.data.listNotes.items;
await Promise.all(notesFromAPI.map(async note => {
if (note.image) {
const image = await Storage.get(note.image);
note.image = image;
}
return note;
}))
setNotes(apiData.data.listNotes.items);
}
************************
/* Arquivo src/App.js */
************************
async function createNote() {
if (!formData.name || !formData.description) return;
await API.graphql({ query: createNoteMutation, variables: { input: formData } });
if (formData.image) {
const image = await Storage.get(formData.image);
formData.image = image;
}
setNotes([ ...notes, formData ]);
setFormData(initialFormState);
}
************************
/* Arquivo src/App.js */
************************
<input
type="file"
onChange={onChange}
/>
************************
/* Arquivo src/App.js */
************************
{
notes.map(note => (
<div key={note.id || note.name}>
<h2>{note.name}</h2>
<p>{note.description}</p>
<button onClick={() => deleteNote(note)}>Delete note</button>
{
note.image && <img src={note.image} style={{width: 400}} alt=""/>
}
</div>
))
}
amplify push --y
npm start
amplify remove auth
? Choose the resource you would want to remove: <your-service-name>
amplify push
amplify delete
npm install @material-ui/core
npm install @material-ui/icons
*******************************
/* Arquivo src/App.css Final */
*******************************
.App {
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
align-self: center;
align-content: center;
padding: 20px;
margin: auto;
width: 70%;
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
.App-header {
background-color: #282c34;
min-height: 100vh;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
Ela recebe o nome das classes, tags que usaremos no código. Por exemplo:
Para utilizarmos os componentes do material-ui, precisamos importá-los. No meu caso fiz separadamente, mas pode ser feito com mais de um item em linha.
Modificamos o retorno da função do app que será exibido na tela, inserindo os componentes do material-ui adequando assim nossa app.
******************************
/* Arquivo src/App.js Final */
******************************
import React, { useState, useEffect } from 'react';
import './App.css';
import { API, Storage } from 'aws-amplify';
import { withAuthenticator, AmplifySignOut } from '@aws-amplify/ui-react';
import { listNotes } from './graphql/queries';
import { createNote as createNoteMutation, deleteNote as deleteNoteMutation } from './graphql/mutations';
import { makeStyles } from '@material-ui/core/styles';
import List from '@material-ui/core/List';
import ListItem from '@material-ui/core/ListItem';
import Divider from '@material-ui/core/Divider';
import ListItemText from '@material-ui/core/ListItemText';
import ListItemAvatar from '@material-ui/core/ListItemAvatar';
import Avatar from '@material-ui/core/Avatar';
import Typography from '@material-ui/core/Typography';
import Grid from '@material-ui/core/Grid';
import IconButton from '@material-ui/core/IconButton';
import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction';
import DeleteIcon from '@material-ui/icons/Delete';
import AddIcon from "@material-ui/icons/Add";
import TextField from '@material-ui/core/TextField';
import Box from '@material-ui/core/Box';
import { Fab } from "@material-ui/core";
import PhotoCamera from '@material-ui/icons/PhotoCamera';
const useStyles = makeStyles((theme) => ({
root: {
width: '100%',
maxWidth: '36ch',
backgroundColor: theme.palette.background.paper,
margin: theme.spacing(3),
},
itens: {
width: '90%',
margin: '10px',
padding: '10px',
},
inline: {
display: 'inline',
},
input: {
display: 'none',
},
textField: {
marginLeft: theme.spacing(1),
marginRight: theme.spacing(1),
width: '25ch',
color: "#2882F8",
},
div: {
color: "#FFF",
backgroundColor: "#f29316",
margin: '10px',
marginBottom: '20px',
padding: '15px',
},
campos: {
margin: '10px',
padding: '10px',
},
camera: {
color: "#f29316",
}
}));
const initialFormState = { name: '', description: '' }
function App() {
const [notes, setNotes] = useState([]);
const [formData, setFormData] = useState(initialFormState);
useEffect(() => {
fetchNotes();
}, []);
async function fetchNotes() {
const apiData = await API.graphql({ query: listNotes });
const notesFromAPI = apiData.data.listNotes.items;
await Promise.all(notesFromAPI.map(async note => {
if (note.image) {
const image = await Storage.get(note.image);
note.image = image;
}
return note;
}))
setNotes(apiData.data.listNotes.items);
}
async function createNote() {
if (!formData.name || !formData.description) return;
await API.graphql({ query: createNoteMutation, variables: { input: formData } });
if (formData.image) {
const image = await Storage.get(formData.image);
formData.image = image;
}
setNotes([ ...notes, formData ]);
setFormData(initialFormState);
}
async function deleteNote({ id }) {
const newNotesArray = notes.filter(note => note.id !== id);
setNotes(newNotesArray);
await API.graphql({ query: deleteNoteMutation, variables: { input: { id } }});
}
async function onChange(e) {
if (!e.target.files[0]) return
const file = e.target.files[0];
setFormData({ ...formData, image: file.name });
await Storage.put(file.name, file);
fetchNotes();
}
const classes = useStyles();
return (
<div className="App">
<Box boxShadow={4} width="auto">
<div className={classes.div}>
<Typography variant="h4" component="h4" >
Listagem de notas
</Typography>
</div>
<div className={classes.campos}>
<TextField
id="filled-basic"
label="Título"
variant="filled"
className={classes.textField}
onChange={e => setFormData({ ...formData, 'name': e.target.value})}
placeholder="Título"
value={formData.name}
/>
<TextField
id="filled-basic"
label="Descrição"
variant="filled"
className={classes.textField}
onChange={e => setFormData({ ...formData, 'description': e.target.value})}
placeholder="Descrição"
value={formData.description}
/>
<input accept="image/*" className={classes.input} id="icon-button-file" type="file" onChange={onChange}/>
<label htmlFor="icon-button-file">
<IconButton className={classes.camera} aria-label="upload picture" component="span">
<PhotoCamera />
</IconButton>
</label>
{/*
<label htmlFor="upload-photo">
<input
style={{ display: "none" }}
id="upload-photo"
name="upload-photo"
type="file"
onChange={onChange}
/>
<Fab
color="secondary"
size="small"
component="span"
aria-label="add"
variant="extended">
<AddIcon /> Procurar imagem
</Fab>
</label>
*/}
<Fab
className={classes.camera}
size="small"
component="span"
aria-label="add"
onClick={createNote}
>
<AddIcon />
</Fab>
</div>
<List className={classes.itens}>
<Grid item xs={12} md={12}>
{
notes.map(note => (
<div key={note.id || note.name}>
<ListItem alignItems="flex-start">
<ListItemAvatar>
{
note.image &&
<Avatar src={note.image} alt=""/>
}
</ListItemAvatar>
<ListItemText
primary={note.name}
secondary={
<React.Fragment>
<Typography
component="span"
variant="body2"
className={classes.inline}
color="textPrimary"
>
{note.description}
</Typography>
</React.Fragment>
}
/>
<ListItemSecondaryAction>
<IconButton edge="end" aria-label="delete" onClick={() => deleteNote(note)} variant="contained" color="secondary">
<DeleteIcon />
</IconButton>
</ListItemSecondaryAction>
</ListItem>
<Divider variant="inset" component="li" />
</div>
))
}
</Grid>
</List>
<AmplifySignOut />
</Box>
</div>
);
}
export default withAuthenticator(App);
ADS - HTML5, CSS3, JS, React, AWS - 5o. módulo.
Modelo e formato elaborado por profa. Ana Paula Müller Giancoli - BSD 2-Clause License. - Julho.2021.